package com.hlkj.warboot.module_wx.configurer.Intercept;

import com.hlkj.warboot.configurer.properties.WxProperties;
import com.hlkj.warboot.configurer.util.*;
import com.hlkj.warboot.configurer.util.base.BaseAccessLimitUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.concurrent.TimeUnit;

/**
 * @Description: TODO 微信活动.抽奖限流拦截器
 * @Author: 李星 lixing_java@163.com
 * @Date: 2020/2/20 20:39
 */
@Component
public class DrawLimitIntercept implements HandlerInterceptor {
    private final RedisTemplate redisTemplate;

    public DrawLimitIntercept(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * @Description 前置拦截
     * @Author 李星
     * @Date 2020/2/20 20:43
     * @Param [request, response, handler]
     * @return boolean
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /*
         * @Description TODO 接口限流
         *               1、对接口本身进行拦截，默认接口在【60】秒内被访问上限【60】次
         *               2、对访问接口的ip进行拦截，默认每个ip在【60】秒内访问接口上限【10】次
         */
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            BaseAccessLimitUtil baseAccessLimitUtil = handlerMethod.getMethodAnnotation(BaseAccessLimitUtil.class);
            if (baseAccessLimitUtil != null) {
                String remoteAddr = "warboot:wx:remoteAddr:" + RequestUtil.getIpAddr(request);
                String requestURI = "warboot:wx:requestURI:" + request.getServletPath();
                String errorUrl = RequestUtil.getBaseErrorUrl(request, "/wx/base/error");
                /*
                 * @Description TODO 是否微信客户端打开
                 */
                if (baseAccessLimitUtil.isWxChat() && !UserAgentUtil.isWechat(request)) {
                    response.sendRedirect(errorUrl + "?code=1");
                    return false;
                }
                if (!baseAccessLimitUtil.isWxChat()) {
                    return true;
                }
                // TODO 1 当前接口在限流区间内被访问的次数
                Long requestUrlCount = redisTemplate.opsForValue().increment(requestURI, 1);
                if (requestUrlCount == 1) {
                    // 当前接口在当前限流区间内被首次访问，根据注解设置其过期时间
                    redisTemplate.expire(requestURI, baseAccessLimitUtil.interfaceSeconds(), TimeUnit.SECONDS);
                }
                if (requestUrlCount > baseAccessLimitUtil.interfaceMaxCount()) {
                    response.sendRedirect(errorUrl + "?code=4&message=" + URLEncoder.encode(MessageFormat.format("当前人数较多,请在{0}秒后再来", redisTemplate.getExpire(requestURI, TimeUnit.SECONDS)), "UTF-8"));
                    return false;
                }
                // TODO 2 当前ip在当前限流区间内访问接口的次数
                Long ipCount = redisTemplate.opsForValue().increment(remoteAddr, 1);
                if (ipCount == 1) {
                    // 当前ip在当前限流区间内首次访问接口，根据注解设置其过期时间
                    redisTemplate.expire(remoteAddr, baseAccessLimitUtil.ipSeconds(), TimeUnit.SECONDS);
                    return true;
                }
                if (ipCount > baseAccessLimitUtil.ipMaxCount()) {
                    // 当前ip在当前限流区间内访问接口超限，提示剩余等待时间
                    redisTemplate.opsForValue().decrement(requestURI, 1); // 当前ip访问无效，该接口被访问次数-1
                    response.sendRedirect(errorUrl + "?code=5&message=" + URLEncoder.encode(MessageFormat.format("客官休息一下,{0}秒后再来", redisTemplate.getExpire(remoteAddr, TimeUnit.SECONDS)), "UTF-8"));
                    return false;
                } else {
                    return true;
                }
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
